package com.example.gobang.game;

import com.example.gobang.GobangApplication;
import com.example.gobang.mapper.UserMapper;
import com.example.gobang.model.User;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.UUID;

//这个类就表示一个游戏房间
@Data
public class Room {
    //使用字符串类型来表示,方便生成唯一值
    private String roomId;

    private User user1;
    private User user2;

    //哪个玩家是先手方
    private int whiteUser;

    private static  final int MAX_ROW = 15;
    private static  final int MAX_COL = 15;


    //这个二维数组用来表示棋盘
    //约定:
    //1.使用 0 表示当前位置未落子 初始化好的 int 二维数组 ,就相当于全 0
    //2.使用 1 表示 user1 的落子位置
    //3.使用 2 表示 user2 的落子位置
    private int board[][] = new int[MAX_ROW][MAX_COL];
    
    //创建 ObjectMapper 用来转换 JSON
    private ObjectMapper objectMapper = new ObjectMapper();


    private OnlineUserStatus onlineUserStatus;


    private RoomManager roomManager;

    private UserMapper userMapper;

    //通过这个方法来处理一次落子的操作
    //要做的事情:
    //1.记录当前落子的位置
    //2.进行胜负判定
    //3.给客户端返回响应
    public  void putChess(String jsonString) throws IOException {
        //1.记录当前落子的位置
        GameRequest request = objectMapper.readValue(jsonString,GameRequest.class);
        GameResponse response = new GameResponse();
        //当前这个字是玩家1落的还是玩家2落的.根据这个玩家1 和 玩家2 来决定往数组中是写1 还是写 2
        int chess = request.getUserId() == user1.getUserId() ? 1 : 2;
        int row = request.getRow();
        int col = request.getCol();
        if(board[row][col] != 0){
            //在客户端已经针对重复落子进行判定过了,此处为了程序更加稳健,在服务再判定一下
            System.out.println("当前位置 (" + row + ", " + col + ") 已经有子了!");
            return;
        }
        board[row][col] = chess;
        //2.打印出当前的棋盘信息,方便来观察局势,也方便后面验证胜负关系的判定
        printBoard();

        //3.进行胜负判定
        int winner = checkWinner(row,col,chess);
        //4.给房间中的所有客户端都返回响应
        response.setMessage("putChess");
        response.setUserId(request.getUserId());
        response.setRow(row);
        response.setCol(col);
        response.setWinner(winner);

        //要想给用户发送 websocket 数据,就需要获取到这个用户的 websocketSession
        WebSocketSession session1 = onlineUserStatus.getFromGameRoom(user1.getUserId());
        WebSocketSession session2 = onlineUserStatus.getFromGameRoom(user2.getUserId());
        //万一当前查到的会话为空(玩家已经下线了)特殊处理一下
        if(session1 == null ){
            //玩家1已经下线了.直接认为玩家2获胜
            response.setWinner(user2.getUserId());
            System.out.println("玩家1 掉线");
        }
        if(session2 == null){
            //玩家2已经下线.直接认为玩家1获胜
            response.setWinner(user1.getUserId());
            System.out.println("玩家2 掉线");
        }
        //把响应构造成 JSON 字符串,通过 session 进行传输
        String respJson = objectMapper.writeValueAsString(response);
        if(session1 != null){
            session1.sendMessage(new TextMessage(respJson));
        }
        if(session2 != null){
            session2.sendMessage(new TextMessage(respJson));
        }

        //5.如果当前胜负已分,此时这个房间就失去了存在的意义,就可以直接销毁房间
        if(response.getWinner() != 0){
            //胜负已分
            System.out.println("游戏结束! 房间即将销毁! roomId = " + roomId + "");
            //更新获胜方和失败方的信息
            int winUserId = response.getWinner();
            int lostUserId = response.getWinner() == user1.getUserId() ? user2.getUserId() : user1.getUserId();
            userMapper.userWin(winUserId);
            userMapper.userLose(lostUserId);
            roomManager.remove(roomId,user1.getUserId(),user2.getUserId());
        }

        


    }

    private void printBoard(){
        //打印棋盘信息
        System.out.println("[打印棋盘信息] " + roomId);
        System.out.println("==========================");
        for (int r = 0; r < MAX_ROW; r++) {
            for (int c = 0; c < MAX_COL; c++) {
                //针对一行之内的若干列,不要打印换行
                System.out.print(board[r][c] + " ");
            }
            //每遍历完一行之后,再打印换行
            System.out.println();

        }
        System.out.println("==========================");
    }



    //使用这个方法来判定当前落子是否分出胜负
    //约定如果玩家1 获胜,就返回 玩家1 的 userId
    //如果玩家2 获胜,就返回玩家2 的 useId
    //如果胜负未分,就返回 0
    private int checkWinner(int row, int col,int chess) {
        //检查所有的行
        //先遍历这五种情况
        for (int c = col - 4; c <= col ; c++) {
            //针对其中的一种情况,来判定这五个子是不是连在一起了
            //不光是这五个子得连着,而且还得和玩家落的子是一样的~
            try{
                if(board[row][c] == chess && board[row][c + 1] == chess
                        && board[row][c + 2] == chess
                        && board[row][c + 3] == chess
                        && board[row][c + 4] == chess){
                    //构成了五子连珠! 胜负已分
                    return  chess == 1 ? user1.getUserId() : user2.getUserId();
                }
            }catch (ArrayIndexOutOfBoundsException e){
                //如果出现数组下标越界的情况,就在这里直接忽略这个异常
                continue;
            }


        }

        //检查所有列
        for (int r = row - 4; r <= row ; r++) {
            //针对其中的一种情况,来判定这五个子是不是连在一起了
            //不光是这五个子得连着,而且还得和玩家落的子是一样的~
            try{
                if(board[r][col] == chess && board[r + 1][col] == chess
                        && board[r + 2][col] == chess
                        && board[r + 3][col] == chess
                        && board[r + 4][col] == chess){
                    //构成了五子连珠! 胜负已分
                    return  chess == 1 ? user1.getUserId() : user2.getUserId();
                }
            }catch (ArrayIndexOutOfBoundsException e){
                //如果出现数组下标越界的情况,就在这里直接忽略这个异常
                continue;
            }


        }
        for(int r = row -4,c = col -4; r <= row && c <= col; r++,c++ ){

            try{
                if(board[r][c] == chess
                        && board[r + 1][c + 1] == chess
                        && board[r + 2][c + 2] == chess
                        && board[r + 3][c + 3] == chess
                        && board[r + 4][c + 4] == chess){
                    //构成了五子连珠! 胜负已分
                    return  chess == 1 ? user1.getUserId() : user2.getUserId();
                }
            }catch (ArrayIndexOutOfBoundsException e){
                //如果出现数组下标越界的情况,就在这里直接忽略这个异常
                continue;
            }
        }

        for(int r = row -4,c = col + 4; r <= row && c >= col; r++,c-- ){

            try{
                if(board[r][c] == chess
                        && board[r + 1][c - 1] == chess
                        && board[r + 2][c - 2] == chess
                        && board[r + 3][c - 3] == chess
                        && board[r + 4][c - 4] == chess){
                    //构成了五子连珠! 胜负已分
                    return  chess == 1 ? user1.getUserId() : user2.getUserId();
                }
            }catch (ArrayIndexOutOfBoundsException e){
                //如果出现数组下标越界的情况,就在这里直接忽略这个异常
                continue;
            }
        }

        //胜负未分,就直接返回了
        return 0;

    }

    public Room(){
        //构造 Room 的时候生成一个唯一的字符串表示房间 id
        //使用 UUID 来作为房间 id
        roomId = UUID.randomUUID().toString();

        //通过入口类中记录的 context 来手动获取到前面的 RoomManager 和 onlineUserStatus
        onlineUserStatus = GobangApplication.context.getBean(OnlineUserStatus.class);
        roomManager = GobangApplication.context.getBean(RoomManager.class);
        userMapper = GobangApplication.context.getBean(UserMapper.class);
    }
}
